#include <render\levels\tile.h>
#include <render\threads\downloadManager.h>
#include <network\streamingClient.h>

#include <iostream>
#include <cmath>

using namespace std;

void
tile::selectedToDownload() {
  terrain_state = DOWNLOADING;
}

void 
tile::terrainDownloaded() {
  download_mutex.lock();

  if (ter_cache->loadTerrainRows()) {
    terrain_state = AVAILABLE;
  }
  else {
    terrain_state = MISSING;
  }

  download_mutex.unlock();

  // notify view resolver
}

boost::shared_ptr<tile> * 
tile::searchForCoarserTile(vector<boost::shared_ptr<tile> *> *coarse_tiles, vec2i finer_tile_id) {

  vec2i coarse_tile_id = finer_tile_id / 2;

  for (int i = 0; i != coarse_tiles->size(); i++) {
    if ((*coarse_tiles)[i]->get()->equals(coarse_tile_id)) {
      return (*coarse_tiles)[i];
    }
  }

  return NULL;
}

bool
tile::generateTerrainForFinerTile(tile *finer_tile, vec2i finer_tile_id, int finer_level) {

  vec2i size = mi_state->info().terr_tile_size;

  unsigned short **rows = ter_cache->getTerrainRows();
  if (rows) {
    
    unsigned short **new_rows = new unsigned short *[size[1]];

    int from_y = 0;
    int from_x = 0;

    for (int y = from_y; y != from_y + (size[1] / 2); y++) {
      for (int x = from_x; x != from_x +(size[0] / 2); x++) {

      }
    }
    delete [] new_rows;
  }  

  return false;
}

unsigned short **
tile::getTerrainRows() {
  return ter_cache->getTerrainRows();
}

unsigned char **
tile::getTextureRows() {
  return tex_cache->getTextureRows();
}

tile::tile(vector<vector<boost::shared_ptr<tile> *> > *all_loaded_tiles, vec2i to_position_l_, vec2i tile_id_, vec2i start_position_, vec2i patch_size_, int level_, modelInfoState *mi_state_) {

  mi_state = mi_state_;

  level = level_;

  patch_size = patch_size_;

  start_position = start_position_;

  tile_id = tile_id_;
  viewer_position = to_position_l_;

  vec2i diff = viewer_position - tile_id;
  distance = (float) sqrt((double) (diff.x * diff.x + diff.y * diff.y));

  ter_cache = static_cast<terrainCacheKey *>(cacheKeysFactory::instance().createCacheKey(CACHE_KEY_TERRAIN, &streamingClient::instance().connection()->getSession()));
  ter_cache->init(tile_id.x, tile_id.y, level);

  if (ter_cache->loadTerrainRows()) {
    terrain_state = AVAILABLE;
  }
  else { // try to generate terrain from previous coarser terrain tile

    // search coarser parent
    if (level < all_loaded_tiles->size() - 1) {
      boost::shared_ptr<tile> *coarse_tile = searchForCoarserTile(&(*all_loaded_tiles)[level + 1], tile_id);
      if (coarse_tile) {
        if (coarse_tile->get()->generateTerrainForFinerTile(this, tile_id, level)) {
          texture_state = GENERATED_FROM_COARSER;
        }
        else {
          terrain_state = TO_BE_DOWNLOADED;
        }
      }
      else {
        terrain_state = TO_BE_DOWNLOADED;
      }
    }
    else {
      terrain_state = TO_BE_DOWNLOADED;
    }
  }

  
  tex_cache = static_cast<textureCacheKey *>(cacheKeysFactory::instance().createCacheKey(CACHE_KEY_TEXTURE, &streamingClient::instance().connection()->getSession()));
  tex_cache->init(tile_id.x, tile_id.y, level);

  if (tex_cache->loadTextureRows()) {
    texture_state = AVAILABLE;
  }
  else { // try to generate terrain from previous coarser terrain tile

    // search coarser parent
    //if (level < all_loaded_tiles->size() - 1) {
    //  boost::shared_ptr<tile> *coarse_tile = searchForCoarserTile(&(*all_loaded_tiles)[level + 1], tile_id);
    //  if (coarse_tile) {
        //if (coarse_tile->get()->generateTerrainForFinerTile(this, tile_id, level)) {
        //  texture_state = GENERATED_FROM_COARSER;
        //}
        //else {
        //  terrain_state = TO_BE_DOWNLOADED;
        //}
      //}
      //else {
        //terrain_state = TO_BE_DOWNLOADED;
     // }
    //}
    //else {
     // terrain_state = TO_BE_DOWNLOADED;
    //}
  }
}

tile::~tile() {
  if (ter_cache) {
    ter_cache->clear();
    delete ter_cache;
  }

  if (tex_cache) {
    tex_cache->clear();
    delete tex_cache;
  }
}

s_download_query 
tile::getDownloadQuery() {
  s_download_query query;
  
  query.distance = distance;
  query.visible = visible;
  query.tile_id = tile_id;
  query.level = level;

  if (terrain_state == GENERATED_FROM_COARSER || terrain_state == TO_BE_DOWNLOADED) {
    query.request_type = TERRAIN;
  }
  else if (texture_state == GENERATED_FROM_COARSER || texture_state == TO_BE_DOWNLOADED) {
    query.request_type = TEXTURE;
  }
  else 
    query.request_type = NOTHING;  

  return query;
}

bool 
tile::equals(vec2i tile_id_) {
  return tile_id == tile_id_;
}

bool 
tile::isVisible() {
  return visible;
}

void 
tile::setVisible(viewFrustum vf_) {
  vf = vf_;
  
  int half = patch_size[0] / 2;

  vec2i p0 = vec2i(tile_id.x - half, tile_id.y - half) * (int) pow(2.0, level) - start_position;
  vec2i p1 = vec2i(tile_id.x - half, tile_id.y + half) * (int) pow(2.0, level) - start_position;
  vec2i p2 = vec2i(tile_id.x + half, tile_id.y - half) * (int) pow(2.0, level) - start_position;
  vec2i p3 = vec2i(tile_id.x + half, tile_id.y + half) * (int) pow(2.0, level) - start_position;

  visible = false;
  visible =  visible || vf.pointInFrustum(vec3f((float) p0.x, (float) p0.y, 0.0)); if (visible) return;
  visible =  visible || vf.pointInFrustum(vec3f((float) p1.x, (float) p1.y, 0.0)); if (visible) return;
  visible =  visible || vf.pointInFrustum(vec3f((float) p2.x, (float) p2.y, 0.0)); if (visible) return;
  visible =  visible || vf.pointInFrustum(vec3f((float) p3.x, (float) p3.y, 0.0)); if (visible) return;
}